#include "SoftwareVersion.h"

#include <random>
#include <fstream>

#include <QtWidgets>
#include <QThread>
#include <QStackedWidget>
#include <QTabWidget>
#include <QTranslator>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QColorDialog>

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osgText/Text>
#include <osgUtil/Tessellator>
#include <osg/PolygonMode>
#include <osg/ShapeDrawable>
#include <osg/FrontFace>
#include <osg/Array>
#include <osg/CullFace>
#include <osgViewer/ViewerEventHandlers>

#include <TopoDS_Face.hxx>

#include <QtitanRibbon.h>

#include "Registry.h"
#include "Log.h"

#include "QtOSGWidget.h"
#include "ViewerPaperSpace.h"
#include "RibbonXML.h"
#include "ViewerExt.h"
#include "Qt3DPicker.h"

#include "mainwindow.h"
#include "SelectHandler3D.h"
#include "SendPointHandler3D.h"
#include "HighlightObjectUnderMouse.h"
#include "osgQtFindFileCallback.h"
#include "osgQtReadFileCallback.h"
#include "JsonFile.h"
#include "QtJson.h"
#include "TextureManager.h"
//#include "IUserFeedback.h"
#include "ActorTable.h"
#include "GLSLPrograms.h"
#include "ShxStatsHandler.h"
#include "OSGNodeMask.h"
#include "CmdLineCtrl.h"
#include "CmdManager.h"
#include "RegisterCmds.h"
#include "Scene.h"
#include "QtUtility.h"
#include "Polygon.h"
#include "QtOSGControlWidget.h"
#include "ExportOSGDlg.h"
#include "OsgUtil.h"
#include "ImportModelDlg.h"
#include "QtAssistant.h"
#include "BrepJson.h"

#ifdef _DEBUG
#include "Curve2DReadWrite.h"
#include "OcctShapeProp.h"
#include "Operation/OpAdd.h"
#include "Arc2D.h"
#include "LineStrip2D.h"
#include "Spline2D.h"
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#endif

class LogFileHandler : public osg::NotifyHandler
{
public:
	LogFileHandler(const std::wstring &file)
	{
		_log.open(file.c_str());
	}
	virtual ~LogFileHandler() { _log.close(); }
	virtual void notify(osg::NotifySeverity severity, const char *msg)
	{
		if (_log.is_open())
			_log << msg;
	}

protected:
	std::ofstream _log;
};

osg::NotifySeverity osgNotifyLevel(const QString &text)
{
	osg::NotifySeverity ret;
	if (text == "FATAL")
		ret = osg::FATAL;
	else if (text == "WARN")
		ret = osg::WARN;
	else if (text == "NOTICE")
		ret = osg::NOTICE;
	else if (text == "INFO")
		ret = osg::INFO;
	else if (text == "DEBUG_INFO")
		ret = osg::DEBUG_INFO;
	else if (text == "DEBUG_FP")
		ret = osg::DEBUG_FP;
	else
		ret = osg::WARN;
	return ret;
}

template<typename DlgType>
void MainWindow::showDlg()
{
	Registry &reg = Registry::instance();
	auto theOnlyDialog = reg.get<QDialog>(theOnlyDialogID);
	if (!theOnlyDialog)
	{
		auto dlg = new DlgType(this);
		dlg->show();
	}
}

QSlider* getSliderInRibbon(const char* name)
{
	auto& reg = Registry::instance();
	auto sliderControl = static_cast<Qtitan::RibbonSliderControl*>(reg.get<Qtitan::RibbonWidgetControl>(name));
	if (sliderControl)
	{
		auto slider = sliderControl->widget();
		return slider;
	}
	return nullptr;
}

MainWindow::MainWindow()
	: m_undoStack(new QUndoStack(this))
{
	auto tcsOsgLogFile = qEnvironmentVariable("CLOUD_PICTURE_OSG_LOG_FILE");
	if (!tcsOsgLogFile.isEmpty())
	{
		auto wname = tcsOsgLogFile.toStdWString();
		osg::setNotifyHandler(new LogFileHandler(wname));
		auto level = qEnvironmentVariable("CLOUD_PICTURE_OSG_NOTIFY_LEVEL");
		osg::setNotifyLevel(osgNotifyLevel(level));
	}
	//osgDB::Registry::instance()->setFindFileCallback(new osgQtFindFileCallback);
	//osgDB::Registry::instance()->setReadFileCallback(new osgQtReadFileCallback);

	Registry &reg = Registry::instance();
	reg.reg(mainWindowID, this);

	setWindowTitle(tr("三维构件设计"));
	QPixmap pixmap = renderSvgFileToPixmap(QSize(32, 32), ":/res/construct.svg");
	QIcon icon;
	icon.addPixmap(pixmap);
	setWindowIcon(icon);

	ribbonBar()->setQuickAccessBarPosition(RibbonBar::QATopPosition);
	create3DWidget();
	setCentralWidget(m_3dSplitter);

	auto cmdManager = m_widget3D->getCmdManager();
	readRibbonXML(":/res/ribbon" /*-zh.xml*/, [cmdManager](const QString& cmd)
	{
		cmdManager->input(false, CS_CMDLINE);
		cmdManager->input(cmd, CS_CMDLINE);
	}, CONSTRUCTDESIGN_VERSION
	);
	createActions();

	auto actionSystemButton = reg.getAction("SystemButton");
	if (actionSystemButton)
	{
		RibbonSystemPopupBar *popupBar = qobject_cast<RibbonSystemPopupBar *>(actionSystemButton->menu());
		if (RibbonPageSystemRecentFileList *pageRecentFile = popupBar->addPageRecentFile(tr("最近文件")))
		{
			pageRecentFile->setSize(9);
			connect(pageRecentFile, &RibbonPageSystemRecentFileList::openRecentFile, this, &MainWindow::openRecentFile);
			connect(this, &MainWindow::updateRecentFileActions, pageRecentFile, &RibbonPageSystemRecentFileList::updateRecentFileActions);
			QSettings settings("Tonghao", "CloudPictureViewer");
			m_recentFiles = settings.value("recentFiles").toStringList();
			emit updateRecentFileActions(m_recentFiles);
		}
	}

	auto helpDoc = reg.getAction("helpDoc");
	if (helpDoc)
	{
		helpDoc->setShortcut(QKeySequence("F1"));
		helpDoc->setShortcutContext(Qt::ApplicationShortcut);
		connect(helpDoc, &QAction::triggered, this, &MainWindow::showHelpDocument);
	}

	auto undo = reg.getAction("undo");
	if (undo)
	{
		undo->setShortcut(QKeySequence("Ctrl+Z"));
		undo->setShortcutContext(Qt::ApplicationShortcut);
		connect(undo, &QAction::triggered, this, &MainWindow::undo);
		undo->setEnabled(false);
	}

	auto redo = reg.getAction("redo");
	if (redo)
	{
		redo->setShortcut(QKeySequence("Ctrl+Y"));
		redo->setShortcutContext(Qt::ApplicationShortcut);
		connect(redo, &QAction::triggered, this, &MainWindow::redo);
		redo->setEnabled(false);
	}

	auto newFileA = reg.getAction("newFile");
	if (newFileA)
	{
		newFileA->setShortcut(QKeySequence("Ctrl+N"));
		newFileA->setShortcutContext(Qt::ApplicationShortcut);
		connect(newFileA, &QAction::triggered, this, &MainWindow::newFile);
	}

	auto openFileA = reg.getAction("openFile");
	if (openFileA)
	{
		openFileA->setShortcut(QKeySequence("Ctrl+O"));
		openFileA->setShortcutContext(Qt::ApplicationShortcut);
		connect(openFileA, &QAction::triggered, this, &MainWindow::openFile);
	}

	auto closeFileA = reg.getAction("closeFile");
	if (closeFileA)
	{
		connect(closeFileA, &QAction::triggered, this, &MainWindow::closeFile);
	}

	auto saveFileA = reg.getAction("saveFile");
	if (saveFileA)
	{
		saveFileA->setShortcut(QKeySequence("Ctrl+S"));
		saveFileA->setShortcutContext(Qt::ApplicationShortcut);
		//connect(saveFileA, &QAction::triggered, [this]() { saveFile(false); });
		connect(saveFileA, &QAction::triggered, [this]() {
			QString originalFileName;
			if (m_fileName.endsWith(".model.json"))
			{
				originalFileName = m_fileName;
				m_recentFiles.removeAll(originalFileName);
				m_fileName = m_fileName.replace(".model.json", ".mdjson");
				setCurrentFile(m_fileName);
			}
			bool success = saveFile(false);
			if (success && !originalFileName.isEmpty())
			{
				QFile::remove(originalFileName);
			}
		});
	}

	auto saveAsFileA = reg.getAction("saveAsFile");
	if (saveAsFileA)
	{
		saveAsFileA->setShortcut(QKeySequence("Ctrl+Shift+S"));
		saveAsFileA->setShortcutContext(Qt::ApplicationShortcut);
		connect(saveAsFileA, &QAction::triggered, this, &MainWindow::saveAsFile);
	}

	auto ucsMask = reg.getAction("ucsMask");
	if (ucsMask)
	{
		connect(ucsMask, &QAction::triggered, this, &MainWindow::setUCSMask);
	}

	auto ucsMeshMask = reg.getAction("ucsMeshMask");
	if (ucsMeshMask)
	{
		connect(ucsMeshMask, &QAction::triggered, this, &MainWindow::setUCSMeshMask);
	}

	auto showAllFace = reg.getAction("showAllFace");
	if (showAllFace)
	{
		connect(showAllFace, &QAction::triggered, this, &MainWindow::showAllFace);
	}

	auto color = reg.getAction("color");
	if (color)
	{
		connect(color, &QAction::triggered, this, &MainWindow::setCurrentColor);
	}

	auto curve2d = reg.getAction("curve2d");
	if (curve2d)
	{
		connect(curve2d, &QAction::triggered, this, &MainWindow::importCurve2d);
	}

	auto importModel = reg.getAction("importModel");
	if (importModel)
	{
		connect(importModel, &QAction::triggered, this, &MainWindow::importModel);
	}

	auto exportOSG = reg.getAction("exportOSG");
	if (exportOSG)
	{
		connect(exportOSG, &QAction::triggered, this, &MainWindow::exportOSG);
	}

	auto exportBrep = reg.getAction("exportBrep");
	if (exportOSG)
	{
		connect(exportBrep, &QAction::triggered, this, &MainWindow::exportBrep);
	}

	auto slider = getSliderInRibbon("faceAlphaCtrl");
	if (slider)
	{
		QObject::connect(slider, &QSlider::valueChanged, this, [](int value) {
			QToolTip::showText(QCursor::pos(), QString::number(value), nullptr);
			setAlpha((float)value / 100.f);
			Registry &reg = Registry::instance();
			Scene* scene = reg.get<Scene>(constructSceneID);
			if (!scene)
				return;
			scene->updateColor();
		});
	}

	connect(m_undoStack, &QUndoStack::indexChanged, this, &MainWindow::onUndoStackIndexChanged);

	updateCommands();
}

MainWindow::~MainWindow()
{
	// fix crash

	// step to reproduce
	//1. use gate command to create a VillaGate.
	//2. undo
	//3. use wall command to create a wall
	//4. exit program

	// call stack on crash (OSG3.6.2)
	//osg157 - osgd.dll!osg::NotifyStreamBuffer::setCurrentSeverity(osg::NotifySeverity severity) Line 78	C++
	//osg157 - osgd.dll!osg::NotifyStream::setCurrentSeverity(osg::NotifySeverity severity) Line 112	C++
	//osg157 - osgd.dll!osg::notify(const osg::NotifySeverity severity) Line 231	C++
	//osg157 - osgd.dll!osg::GraphicsObjectManager::~GraphicsObjectManager() Line 70	C++

	//code on crash point (_severity == 0xdddddddd)
	//void setCurrentSeverity(osg::NotifySeverity severity)
	//{
	//    if (_severity != severity)
	//    {
	//        sync();
	//        _severity = severity;
	//    }
	//}

	// fix crash(same as above)
	// step to reproduce
	//1. new file.
	//2. new another file
	//3. exit program

	// When osgText::Text is used, it crashes on exit because of remained resources in shader manager. fix it once for all.
	osg::setNotifyLevel(osg::NotifySeverity::ALWAYS);

	osgQtReadFileCallback::release();
}

void MainWindow::retranslate()
{
	setWindowTitle(tr("三维构件设计 - ") + m_fileName);

	auto &reg = Registry::instance();
}

void MainWindow::changeEvent(QEvent *event)
{
	if (event->type() == QEvent::LanguageChange)
	{
		retranslate();
	}
	else
	{
		base::changeEvent(event);
	}
}

void MainWindow::showHelpDocument()
{
	QString filePath = QCoreApplication::applicationDirPath() + "/cd-html/toc.html";
	QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	// this will call saveFile if necessary
	if (closeFile())
	{
		writeSettings();
		event->accept();
	}
	else
	{
		event->ignore();
	}
}

void MainWindow::create3DWidget()
{
	Registry &reg = Registry::instance();

	m_3dSplitter = new QSplitter(Qt::Vertical);
	m_widget3D = new QtOSGWidget();
	reg.reg(widget3DID, m_widget3D);

	QSettings settings(QSettings::UserScope, tr("同豪公司"), tr("三维构件设计"));
	QVariant upperColor = settings.value("upperColor");
	QVariant lowerColor = settings.value("lowerColor");
	QColor upperColor2, lowerColor2;
	if (upperColor.isValid() && lowerColor.isValid())
	{
		upperColor2.setNamedColor(upperColor.toString());
		lowerColor2.setNamedColor(lowerColor.toString());
		m_widget3D->setBkColor(convertColor(upperColor2), convertColor(lowerColor2));
	}
	else
	{
		m_widget3D->setBkColor(convertColor(QColor(0xE1, 0xE1, 0xE1)), convertColor(QColor(0x2C, 0x79, 0x9C)));
	}

	m_widget3D->getSelector()->setPointBoxModeTraversalMask(D2CurveMask | D3CurveMask | D2LineMask | D3LineMask);
	auto cmdManager = m_widget3D->getCmdManager();
	cmdManager->setUndoStack(m_undoStack);
	registerCmds(cmdManager);
	auto osgWidgetWithControl = new QtOSGWidgetWithControl(m_widget3D, true, true, tr("同豪公司"), tr("三维构件设计"));
	m_3dSplitter->addWidget(osgWidgetWithControl);
	auto cmdline3d = new CmdLineCtrl(cmdManager);
	cmdline3d->setObjectName("cmdline3d");
	cmdline3d->setParent(this);
	cmdline3d->prompt(0, tr("命令:"));
	m_3dSplitter->addWidget(cmdline3d);

	m_widget3D->getPointSender()->setSnapPointFlag(SnapPointObject);

	auto hightlighter = new HighlightObjectUnderMouse(cmdManager, m_widget3D);
	m_widget3D->getViewer()->addEventHandler(hightlighter);

	//除了快捷键，其他按键从三维图形窗口转发到命令行控件
	connect(m_widget3D, &QtOSGWidget::keyPressed, cmdline3d, &CmdLineCtrl::processKeyEvent);

	auto osgViewer = m_widget3D->getViewer();
	auto statsH = new ShxStatsHandler;
	statsH->setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
	osgViewer->addEventHandler(statsH);
	auto screenCaptureH = new osgViewer::ScreenCaptureHandler;
	screenCaptureH->setKeyEventTakeScreenShot(osgGA::GUIEventAdapter::KEY_F4);
	screenCaptureH->setKeyEventToggleContinuousCapture(osgGA::GUIEventAdapter::KEY_F5);
	osgViewer->addEventHandler(screenCaptureH);

	m_widget3D->setHelpText(L"ctrl+F1 - 显示/关闭帮助, F3 - 性能统计\nF4 - 截屏, F5 - 开始/停止连续截屏\nF7 - 特征点捕捉, F8 - 正交捕捉\n[ctrl+]左击 - 选择对象, 左键拖拽 - 旋转视图\n鼠标滚轮 - 缩放, shift+鼠标滚轮 - 慢速缩放\n鼠标中键拖拽 - 平移, 鼠标中键双击 - 全图显示");
}


void MainWindow::setLanguage(QAction *sender)
{
	auto lang = sender->property("userdata").toString();
	Registry &reg = Registry::instance();
	reg.setLanguage(lang.toStdString().c_str());
	auto cmdManager = m_widget3D->getCmdManager();
	readRibbonXML(":/res/ribbon" /*-zh.xml*/, [cmdManager](const QString& cmd)
	{
		cmdManager->input(false, CS_CMDLINE);
		cmdManager->input(cmd, CS_CMDLINE);
	}, CONSTRUCTDESIGN_VERSION
	);
}

void MainWindow::updateCommands()
{
	Registry &reg = Registry::instance();
	auto scene = reg.get<Scene>(constructSceneID);
	enableDocumentActionsAndWidgets(scene != nullptr);
}

void MainWindow::onUndoStackIndexChanged(int index)
{
	Registry &reg = Registry::instance();
	//更新三维窗口
	//m_widget3D->update();

	//更新构件树模型，在addChild/removeChild里面发出信号, 在ConstructTreeModel里面接收信号

	//更新undo/redo按钮状态
	auto undo = reg.getAction("undo");
	auto redo = reg.getAction("redo");
	if (undo && redo)
	{
		auto stack = m_undoStack;
		undo->setEnabled(stack->canUndo());
		redo->setEnabled(stack->canRedo());
	}
}

void MainWindow::undo()
{
	auto stack = m_undoStack;
	if (stack->canUndo())
	{
		stack->undo();
	}
}

void MainWindow::redo()
{
	auto stack = m_undoStack;
	if (stack->canRedo())
	{
		stack->redo();
	}
}

bool MainWindow::newFileInner()
{
	if (!closeFile())
	{
		return false;
	}

	Registry &reg = Registry::instance();
	Scene* scene = new Scene(this, m_undoStack);
	reg.reg(constructSceneID, scene);
	auto root3D = scene->getRoot3D();
	auto osgViewer = dynamic_cast<ViewerExt*>(m_widget3D->getViewer());
	setScene(nullptr, scene);
	auto pickHandler3D = m_widget3D->getPicker();
	pickHandler3D->init(osgViewer, 5);
	m_widget3D->getCameraManipulator()->setNode(root3D);
	scene->setPicker(pickHandler3D);

	m_widget3D->getPointSender()->setSnapPointCallback([scene](PointMatrix3D & point) { return scene->snapPoint(point); });

	m_widget3D->getCmdManager()->enableInput(true);
	updateCommands();
	auto cmdline3d = findChild<CmdLineCtrl*>("cmdline3d");
	cmdline3d->clearOutput();
	return true;
}

bool MainWindow::newFile()
{
	if (!newFileInner())
	{
		return false;
	}
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (scene)
	{
		// 修复有时有些UCS不显示的错误
		m_widget3D->pauseRenderThread();
		scene->newFile();
		m_widget3D->resumeRenderThread();
	}
	return true;
}

bool MainWindow::openFile()
{
	if (!newFileInner())
	{
		return false;
	}
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (scene)
	{
		auto fileName = QFileDialog::getOpenFileName(this, tr("打开文件"), "", tr("设计文件(*.mdjson *.model.json)"));
		if (fileName.isEmpty())
			return false;

		return innerOpenFile(fileName);
	}
	return false;
}

bool MainWindow::closeFile()
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (scene)
	{
		auto stack = m_undoStack;
		if (!stack->isClean())
		{
			int ret = QMessageBox::warning(this, tr("三维建模程序"),
				tr("文件已经修改，\n"
					"需要保存吗？"),
				QMessageBox::Save | QMessageBox::Discard
				| QMessageBox::Cancel,
				QMessageBox::Save);
			if (QMessageBox::Save == ret)
			{
				if (!saveFile(false))
					return false;
			}
			else if (QMessageBox::Cancel == ret)
			{
				return false;
			}
		}
		// unregister scene
		reg.reg(constructSceneID, nullptr);

		setScene(scene, nullptr);

		m_widget3D->getPointSender()->setSnapPointCallback(nullptr);

		m_undoStack->clear();
		scene->blockSignals(true);
		delete scene;

		m_widget3D->getCmdManager()->enableInput(false);
		updateCommands();
	}
	m_fileName.clear();
	setWindowTitle(tr("三维构件设计 - "));
	return true;
}

bool MainWindow::saveFile(bool saveAs)
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return true;
	auto stack = m_undoStack;
	// 去掉脏标志检查，总是保存。因为有些修改没有计入undo，但是却会被保存。
	//if (stack->isClean() && !saveAs)
	//{
	//	return true;
	//}
	if (m_fileName.isEmpty())
	{
		return saveAsFile();
	}
	QFile saveFile(m_fileName);

	if (!saveFile.open(QIODevice::WriteOnly))
	{
		LOG_ERR("Can't save file %s", m_fileName.toLocal8Bit().data());
		return false;
	}

	QJsonObject docObj;
	QJsonObject sceneJson;
	// 在save前后加上pause/resum，可能更加安全
	m_widget3D->pauseRenderThread();
	scene->save(sceneJson);
	m_widget3D->resumeRenderThread();
	docObj["scene"] = sceneJson;
	QJsonDocument saveDoc(docObj);
	saveFile.write(saveDoc.toJson());
	stack->setClean();
	return true;
}

bool MainWindow::saveAsFile()
{
	auto fileName = QFileDialog::getSaveFileName(this, tr("另存为"), "", tr("设计文件(*.mdjson)"));
	if (fileName.isEmpty()) // user canceled
		return true;
	setCurrentFile(fileName);
	return saveFile(true);
}

void MainWindow::openRecentFile(const QString &fileName)
{
	if (!closeFile())
	{
		return;
	}

	if (!newFileInner())
	{
		return;
	}

	innerOpenFile(fileName);
}

int getLeafCount(IRoot* root);
bool MainWindow::innerOpenFile(const QString &fileName)
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (scene)
	{
		QFile loadFile(fileName);
		if (!loadFile.open(QIODevice::ReadOnly)) {
			LOG_ERR("Can't open file %s", fileName.toLocal8Bit().data());
			removeFileFromRecentList(fileName);
			return false;
		}

		QByteArray saveData = loadFile.readAll();
		QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
		auto docObj = loadDoc.object();
		// 在load前后加上pause/resum，来修复打开文件时可能发生的绘制线程崩溃
		m_widget3D->pauseRenderThread();
		scene->load(docObj["scene"].toObject());
		m_widget3D->resumeRenderThread();

		m_widget3D->getCmdManager()->enableInput(true);
		updateCommands();

		auto root = m_widget3D->getIRoot();
		int leafCount = getLeafCount(root);
		m_widget3D->getPointSender()->setSnapPointFlag(leafCount < 10000 ? SnapPointObject : 0);

		setCurrentFile(fileName);

		return true;
	}
	return false;
}

void MainWindow::setCurrentFile(const QString &fileName)
{
	m_fileName = fileName;
	setWindowTitle(tr("三维构件设计 - ") + fileName);

	m_recentFiles.removeAll(fileName);
	m_recentFiles.prepend(fileName);
	while (m_recentFiles.size() > 9)
		m_recentFiles.removeLast();

	emit updateRecentFileActions(m_recentFiles);
}

void MainWindow::removeFileFromRecentList(const QString &fileName)
{
	m_recentFiles.removeAll(fileName);
	emit updateRecentFileActions(m_recentFiles);
}

void MainWindow::writeSettings()
{
	QSettings settings("Tonghao", "CloudPictureViewer");
	settings.setValue("recentFiles", m_recentFiles);
}

void MainWindow::setScene(Scene * oldScene, Scene * newScene)
{
	Registry &reg = Registry::instance();
	auto osgViewer = dynamic_cast<ViewerExt*>(m_widget3D->getViewer());
	m_widget3D->pauseRenderThread();
	if (oldScene)
	{
		osgViewer->getSceneData()->asGroup()->removeChild(oldScene->getRoot3D());
		osgViewer->removePostDraw(oldScene->getSnapPoint());
		m_widget3D->setIRoot(nullptr);
	}
	if (newScene)
	{
		osgViewer->getSceneData()->asGroup()->addChild(newScene->getRoot3D());
		osgViewer->addPostDraw(newScene->getSnapPoint());
		m_widget3D->setIRoot(newScene);
		//static_cast<MyManipulator*>(m_manipulator3D.get())->toView(MyManipulator::XYZ, m_osgViewer->getCamera());
	}
	m_widget3D->resumeRenderThread();
}

void MainWindow::setCurrentColor()
{
	QColor color = QColorDialog::getColor(m_color, this);
	if (color.isValid())
	{
		m_color = color;
	}
}

#ifdef _DEBUG
void fromCurve(Handle(Geom2d_BSplineCurve) curve,
	int& m_Flag,
	int& m_Degree,
	std::vector<double>& m_Knots,
	std::vector<double>& m_Weights,
	std::vector<osg::Vec3d>& m_ControlPoints);
#endif
void MainWindow::importCurve2d()
{
#ifdef _DEBUG
	std::vector<Handle(Geom2d_Curve)> curves;
	bool success = readCurve2ds(curves, "D:/curve2d.brep2d");
	if (success)
	{
		Registry &reg = Registry::instance();
		Scene* scene = reg.get<Scene>(constructSceneID);
		auto xoy = scene->getUCS()[(int)UCSKind::XOY];
		std::set<std::shared_ptr<INode>> children;
		for (const auto& curve : curves)
		{
			auto bc = basisCurve(curve);
			if (bc->DynamicType() == STANDARD_TYPE(Geom2d_Line))
			{
				auto trim = Handle(Geom2d_TrimmedCurve)::DownCast(curve);
				if (trim)
				{
					auto line2 = std::make_shared<LineStrip2D>(xoy.get());
					std::vector<osg::Vec3d> points;
					auto sp = trim->StartPoint();
					auto ep = trim->EndPoint();
					points.push_back(osg::Vec3d(sp.X(), sp.Y(), 0));
					points.push_back(osg::Vec3d(ep.X(), ep.Y(), 0));
					line2->setPoints(points);
					children.insert(line2);
				}
			}
			else if (bc->DynamicType() == STANDARD_TYPE(Geom2d_BSplineCurve))
			{
				auto spline = Handle(Geom2d_BSplineCurve)::DownCast(bc);
				auto spline2 = std::make_shared<Spline2D>(xoy.get());
				// 这里不考虑Geom2d_TrimmedCurve，因为只是调试bug用途
				int m_Flag;
				int m_Degree;
				std::vector<double> m_Knots;
				std::vector<double> m_Weights;
				std::vector<osg::Vec3d> m_ControlPoints;
				fromCurve(spline, m_Flag, m_Degree, m_Knots, m_Weights, m_ControlPoints);
				spline2->setFlag(m_Flag);
				spline2->setDegree(m_Degree);
				spline2->setKnots(m_Knots);
				spline2->setWeights(m_Weights);
				spline2->setCtrlPoints(m_ControlPoints);
				children.insert(spline2);
			}
			else if (bc->DynamicType() == STANDARD_TYPE(Geom2d_Circle))
			{
				auto circle = Handle(Geom2d_Circle)::DownCast(bc);
				auto arc = std::make_shared<Arc2D>(xoy.get());
				auto center = circle->Location();
				auto trim = Handle(Geom2d_TrimmedCurve)::DownCast(curve);
				double u1, u2;
				if (trim)
				{
					u1 = trim->FirstParameter();
					u2 = trim->LastParameter();
				}
				else
				{
					u1 = 0;
					u2 = 2 * M_PI;
				}
				auto Vx = circle->Position().XDirection();
				auto Vy = circle->Position().YDirection();
				double value = Vx.Crossed(Vy);
				bool rightHand = value >= 0;
				if (!rightHand)
				{
					u1 = circle->ReversedParameter(u1);
					u2 = circle->ReversedParameter(u2);
					std::swap(u1, u2);
				}
				arc->set(osg::Vec3d(center.X(), center.Y(), 0), circle->Radius(), u1, u2);
				children.insert(arc);
			}
		}
		if (!children.empty())
		{
			m_undoStack->push(new OpAdd(children));
		}
	}
#endif
}

osg::Vec4 MainWindow::getCurrentColor()
{
	return osg::Vec4(m_color.red() / 255.f, m_color.green() / 255.f, m_color.blue() / 255.f, 1.f);
}

void MainWindow::importModel()
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;
	auto dlg = new ImportModelDlg(this);
	dlg->show();
}

struct LessVec3f
{
	static bool less(const osg::Vec3d& p1, const osg::Vec3d& p2)
	{
		if (!lengthApprox(p1.x(), p2.x()))
		{
			return p1.x() < p2.x();
		}
		if (!lengthApprox(p1.y(), p2.y()))
		{
			return p1.y() < p2.y();
		}
		if (!lengthApprox(p1.z(), p2.z()))
		{
			return p1.z() < p2.z();
		}
		return false;
	}

	bool operator()(const osg::Vec3d& p1, const osg::Vec3d& p2) const
	{
		return less(p1, p2);
	}
};

void MainWindow::exportOSG()
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;

	showDlg<ExportOSGDlg>();
}

void MainWindow::exportBrep()
{
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return ;
	auto fileName = QFileDialog::getSaveFileName(this, tr("保存为"), "", tr("IGS(*.iges *.igs);;STEP(*.step *.stp);;BREP(*.brep *.brp);;JSON文件(*.brepjson)"));
	if (fileName.isEmpty()) // user canceled
		return;
	if (fileName.endsWith(".brep.json", Qt::CaseInsensitive) ||
		fileName.endsWith(".brepjson", Qt::CaseInsensitive))
	{
		QFile saveFile(fileName);
		if (!saveFile.open(QIODevice::WriteOnly))
		{
			LOG_ERR("Can't save file %s", m_fileName.toLocal8Bit().data());
			return;
		}
		QJsonObject docObj = scene->exportBrep();
		QJsonDocument saveDoc(docObj);
		saveFile.write(saveDoc.toJson());
		saveFile.close();
		return;
	}

	auto faces = scene->getFaces();
	if (fileName.endsWith(".step", Qt::CaseInsensitive) ||
		fileName.endsWith(".stp", Qt::CaseInsensitive))
	{
		bool success = ExportConstructToSTEP(faces, fileName.toStdString().c_str());
	}
	else if (fileName.endsWith(".iges", Qt::CaseInsensitive) ||
		fileName.endsWith(".igs", Qt::CaseInsensitive))
	{
		bool success = ExportConstructToIGES(faces, fileName.toStdString().c_str());
	}
	else if (fileName.endsWith(".brep", Qt::CaseInsensitive) ||
		fileName.endsWith(".brp", Qt::CaseInsensitive))
	{
		bool success = ExportConstructToBREP(faces, fileName.toStdString().c_str());
	}
	return;
}

void MainWindow::setUCSMask()
{
	QAction* action = qobject_cast<QAction*>(sender());
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;
	auto ucses = scene->getUCS();
	int nodeMask;
	QString str = tr("隐藏UCS对象");
	if (str.compare(action->text()) == 0)
	{
		nodeMask = 0;
		action->setText(tr("显示UCS对象"));
	}
	else
	{
		nodeMask = ~NoPickMask;
		action->setText(tr("隐藏UCS对象"));
	}
	for (auto& ucs : ucses)
	{
		auto ucsObjects = ucs->getChildren();
		for (auto& ucsObject : ucsObjects)
		{
			ucsObject->getRep3D()->setNodeMask(nodeMask);
		}
	}
}

void MainWindow::setUCSMeshMask()
{
	QAction* action = qobject_cast<QAction*>(sender());
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;
	auto ucses = scene->getUCS();
	int nodeMask = ucses[0]->getMesh()->getNodeMask();
	if (nodeMask == 0)
	{
		nodeMask = NoPickMask;
		action->setText(tr("隐藏UCS网格"));
	}
	else
	{
		nodeMask = 0;
		action->setText(tr("显示UCS网格"));
	}
	for (auto& ucs : ucses)
	{
		auto node = ucs->getMesh();
		node->setNodeMask(nodeMask);
	}
}


void MainWindow::showAllFace()
{
	QAction* action = qobject_cast<QAction*>(sender());
	Registry &reg = Registry::instance();
	Scene* scene = reg.get<Scene>(constructSceneID);
	if (!scene)
		return;
	scene->showAllFace();
}
